package com.jweb.framework.service.sqlmap.impl;
import java.io.Serializable;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;
import org.springframework.util.Assert;

import com.jweb.framework.core.service.sqlmap.SqlMap;
import com.jweb.framework.service.sqlmap.support.SqlSessionUtils;

/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：数据操作实现类
 */
public class SqlMapImpl implements SqlMap {
	
	protected Logger logger = LoggerFactory.getLogger(SqlMapImpl.class);
	private boolean flag ;
	private DataSource dataSource ;
	private PersistenceExceptionTranslator persistenceExceptionTranslator ;
	private SqlSessionFactory sqlSessionFactory ;
	private Cache cache ;
	
	public SqlMapImpl(DataSource dataSource , SqlSessionFactory sqlSessionFactory , PersistenceExceptionTranslator persistenceExceptionTranslator , Cache cache){
		this.dataSource = dataSource ;
		this.sqlSessionFactory = sqlSessionFactory ;
		this.persistenceExceptionTranslator = persistenceExceptionTranslator ;
		if(this.sqlSessionFactory == null){
			throw new IllegalArgumentException("Property 'sqlSessionFactory' is required");
		}
		this.cache = cache ;
	}
	
	private SqlSession openSession(DataSource dataSource , ExecutorType executorType){
		SqlSession sqlSession = SqlSessionUtils.getSqlSession(this.sqlSessionFactory , executorType , this.persistenceExceptionTranslator);
		if(logger.isDebugEnabled()){
			logger.debug("Opened SqlSession [" + sqlSession + "] for iBatis operation");
		}
		return sqlSession ;
	}
	
	private <T> T execute(SqlMapClientCallback action) throws DataAccessException {
		
		Assert.notNull(action , "Callback object must not be null");
		SqlSession sqlSession = openSession(this.dataSource , (this.flag)?ExecutorType.REUSE : ExecutorType.SIMPLE);
		try {
			Object obj1 = action.doInSqlMapClient(sqlSession);
			if(!SqlSessionUtils.isSqlSessionTransactional(sqlSession , this.sqlSessionFactory)){
				sqlSession.commit();
			}
			return (T)obj1 ;
		} catch (Throwable t) {
			Object obj2 = ExceptionUtil.unwrapThrowable(t);
			if((this.persistenceExceptionTranslator != null) && (obj2 instanceof PersistenceException)){
				obj2 = this.persistenceExceptionTranslator.translateExceptionIfPossible((PersistenceException) obj2);
			}
			DataAccessException dae = (DataAccessException)obj2 ;
			logger.info("{}" , dae.getMessage());
			throw dae ;
		} finally{
			SqlSessionUtils.closeSqlSession(sqlSession , this.sqlSessionFactory);
		}
		
	}
	
	/**单条数据查询*/
	public <T> T queryForObject(String sql) throws DataAccessException {
		return queryForObject(sql , null);
	}
	
	public <T> T queryForObject(final String sql , final Object obj) throws DataAccessException {
		return execute( new SqlMapClientCallback<T>() {

			public T doInSqlMapClient(SqlSession sqlSession) {
				
				return (T) sqlSession.selectOne(sql , obj) ;
			}
			
		});
	}
	
	/**单条数据查询+EhCache*/
	public <T> T queryForObjectEhCache(String sql) throws DataAccessException {
		return queryForObjectEhCache(sql , null);
	}
	
	public <T> T queryForObjectEhCache(final String sql , final Object obj) throws DataAccessException {
		String cacheKey = getCacheKey(sql , obj , 0 , 0);
		//int hashKey = hashKey(cacheKey);
		T resultObject = null ;
		logger.debug("EhCache缓存的Key值 ：【" + cacheKey + "】");
		
		Element element = null ;
		
		synchronized (this) {
			//element = this.cache.get(hashKey);
			element = this.cache.get(cacheKey);
			if(element == null){
				logger.debug("EhCache缓存中没有查到相应数据集合！");
				
				// 缓存中没有找到记录，则查询数据库
				resultObject = execute( new SqlMapClientCallback<T>() {

					public T doInSqlMapClient(SqlSession sqlSession) {
						
						return (T) sqlSession.selectOne(sql , obj) ;
					}
					
				});
				
				if(resultObject != null){
					element = new Element(cacheKey , (Serializable)resultObject);
					cache.put(element);
					logger.debug("将查询的数据集合加入缓存， 【key= " + cacheKey + " , cacheName = " + this.cache.getName() + "】");
				}
			}
			else {
				resultObject = (T) element.getValue();
				logger.debug("使用缓存， 【key= " + cacheKey + " , cacheName = " + this.cache.getName() + "】");
			}
		}
		
		return resultObject ;
	}
	
	
	
	/**多条数据查询*/
	public <T> List<T> queryForList(String sql) throws DataAccessException {
		
		return queryForList(sql , null) ;
	}
	
	public <T> List<T> queryForList(final String sql , final Object obj) throws DataAccessException {
		return execute(new SqlMapClientCallback<T>() {

			public T doInSqlMapClient(SqlSession sqlSession) {
				
				return (T) sqlSession.selectList(sql , obj) ;
			}
		});
	}
	
	
	public <T> List<T> queryForList(String sql , int skipResults , int maxResults){
		return queryForList(sql , null , skipResults , maxResults);
	}
	
	public <T> List<T> queryForList(final String sql , final Object obj , final int skipResults , final int maxResults) throws DataAccessException {
		return execute(new SqlMapClientCallback<T>() {

			public T doInSqlMapClient(SqlSession sqlSession) {
				
				return (T) sqlSession.selectList(sql , obj , new RowBounds(skipResults , maxResults)) ;
			}
		});
	}
	
	/**多条数据查询+EhCache*/
	public <T> List<T> queryForListEhCache(String sql) throws DataAccessException {
		
		return queryForListEhCache(sql , null) ;
	}
	
	public <T> List<T> queryForListEhCache(final String sql , final Object obj) throws DataAccessException {
		
		String cacheKey = getCacheKey(sql , obj , 0 , 0);
		//int hashKey = hashKey(cacheKey);
		List<T> resultList = null ;
		logger.debug("EhCache缓存的Key值 ：【" + cacheKey + "】");
		
		Element element = null ;
		
		synchronized (this) {
			element = this.cache.get(cacheKey);
			if(element == null){
				logger.debug("EhCache缓存中没有查到相应数据集合！");
				
				// 缓存中没有找到记录，则查询数据库
				resultList = execute(new SqlMapClientCallback<T>() {

					public T doInSqlMapClient(SqlSession sqlSession) {
						
						return (T) sqlSession.selectList(sql , obj) ;
					}
				}) ;
				
				if((resultList != null) && (resultList.size() != 0)){
					element = new Element(cacheKey , (Serializable)resultList);
					this.cache.put(element);
					logger.debug("将查询的数据集合加入缓存， 【key= " + cacheKey + " , cacheName = " + this.cache.getName() + "】");
				}
			}
			else {
				resultList = (List<T>) element.getValue();
				logger.debug("使用缓存， 【key= " + cacheKey + " , cacheName = " + this.cache.getName() + "】");
			}
		}
		
		return resultList ;
	}
	
	public <T> List<T> queryForListEhCache(String sql , int skipResults , int maxResults){
		return queryForListEhCache(sql , null , skipResults , maxResults);
	}
	
	public <T> List<T> queryForListEhCache(final String sql , final Object obj , final int skipResults , final int maxResults) throws DataAccessException {
		
		String cacheKey = getCacheKey(sql , obj , skipResults , maxResults);
		//int hashKey = hashKey(cacheKey);
		List<T> resultList = null ;
		logger.debug("EhCache缓存的Key值 ：【" + cacheKey + "】");
		
		Element element = null ;
		
		synchronized (this) {
			element = this.cache.get(cacheKey);
			if(element == null){
				logger.debug("EhCache缓存中没有查到相应数据集合！");
				
				// 缓存中没有找到记录，则查询数据库
				resultList = execute(new SqlMapClientCallback<T>() {

					public T doInSqlMapClient(SqlSession sqlSession) {
						
						return (T) sqlSession.selectList(sql , obj , new RowBounds(skipResults , maxResults)) ;
					}
				});
				
				if((resultList != null) && (resultList.size() != 0)){
					element = new Element(cacheKey , (Serializable)resultList);
					this.cache.put(element);
					logger.debug("将查询的数据集合加入缓存， 【key= " + cacheKey + " , cacheName = " + this.cache.getName() + "】");
				}
			}
			else {
				resultList = (List<T>) element.getValue() ;
				logger.debug("使用缓存， 【key= " + cacheKey + " , cacheName = " + this.cache.getName() + "】");
			}
		}
		
		return resultList ;
	}
	
	
	
	
	
	private String getCacheKey(String sqlId , Object obj , int skipResults , int maxResults) {
		StringBuilder sb = new StringBuilder();
		sb.append(sqlId).append(".").append(skipResults).append(".").append(maxResults) ;
		if(obj != null){
			for(Map.Entry<String, Object> entry : ((Map<String, Object>)obj).entrySet()){
				String key = entry.getKey() ;
				Object value = entry.getValue();
				sb.append(".").append(value);
			}
		}
		return sb.toString() ;
	}
	
	private int hashKey(String cacheKey){
		return cacheKey.hashCode() ;
	}
	
	
	public void queryWithRowHandler(final String sql , final Object obj , final ResultHandler resultHandler) throws DataAccessException {
		
		execute(new SqlMapClientCallback() {

			public Object doInSqlMapClient(SqlSession sqlSession) {
				sqlSession.select(sql , obj , resultHandler);
				return null;
			}
		});
		
	}
	
	public void queryWithRowHandler(final String sql , final Object obj , final int skipResult , final int maxResult , final ResultHandler resultHandler) throws DataAccessException {
		execute(new SqlMapClientCallback<Object>() {

			public Object doInSqlMapClient(
					SqlSession sqlSession) {
				sqlSession.select(sql , obj , new RowBounds(skipResult , maxResult), resultHandler);
				return null;
			}
		});
		
	}
	
	
	/**数据插入*/
	public int insert(String sql) throws DataAccessException {
		return insert(sql , null);
	}
	
	public int insert(final String sql , final Object obj) throws DataAccessException {
		return execute(new SqlMapClientCallback() {

			public Integer doInSqlMapClient(
					SqlSession sqlSession) {
				// TODO Auto-generated method stub
				return sqlSession.insert(sql , obj);
			}
		});
	}
	
	
	/**数据插入 + EhCache*/
	public int insertEhCache(String sql) throws DataAccessException {
		return insertEhCache(sql , null);
	}
	
	public int insertEhCache(final String sql , final Object obj) throws DataAccessException {
		
		int result = execute(new SqlMapClientCallback() {

			public Integer doInSqlMapClient(
					SqlSession sqlSession) {
				// TODO Auto-generated method stub
				return sqlSession.insert(sql , obj);
			}
		});
		
		if(result > 0) {
			logger.debug("更新记录成功，移除缓存中相应记录。");
			updateEhCacheElement(sql);
		}
		
		return result ;
	}
	
	private void updateEhCacheElement(String sql) {
		String[] sqlIds = sql.split("\\.");
		String sqlId = sqlIds[0];
		List keys = this.cache.getKeys();
		int size = keys.size();
		
		for(int i = 0 ; i < size ; i++){
			String cacheKey = (String) keys.get(i);
			if(cacheKey.startsWith(sqlId)){
				this.cache.remove(cacheKey);
				logger.debug("从缓存中移除记录，cacheKey = 【" + cacheKey + "】");
			}
		}
	}
	
	
	/**数据更新*/
	public int update(String sql) throws DataAccessException {
		return update(sql , null);
	}
	
	public int update(final String sql , final Object obj) throws DataAccessException {
		return execute(new SqlMapClientCallback() {

			public Integer doInSqlMapClient(SqlSession sqlSession) {
				
				return sqlSession.update(sql , obj);
			}
		});
		
	}
	
	public void update( String sql ,  Object obj, int requiredRowsAffected) throws DataAccessException {
		int i = update(sql , obj);
		if(i != requiredRowsAffected){
			throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(sql ,requiredRowsAffected , i);
		}
	}
	
	/**数据更新 + EhCache*/
	public int updateEhCache(String sql) throws DataAccessException {
		return updateEhCache(sql , null);
	}
	
	public int updateEhCache(final String sql , final Object obj) throws DataAccessException {
		int result = execute(new SqlMapClientCallback() {

			public Integer doInSqlMapClient(SqlSession sqlSession) {
				
				return sqlSession.update(sql , obj);
			}
		});
		
		if(result > 0) {
			logger.debug("更新记录成功，移除缓存中相应记录。");
			updateEhCacheElement(sql);
		}
		
		return result ;
	}
	
	public void updateEhCache( String sql ,  Object obj, int requiredRowsAffected) throws DataAccessException {
		int i = update(sql , obj);
		if(i != requiredRowsAffected){
			throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(sql ,requiredRowsAffected , i);
		}
		else {
			logger.debug("更新记录成功，移除缓存中相应记录。");
			updateEhCacheElement(sql);
		}
	}
	
	
	/**数据删除*/
	public int delete(String sql) throws DataAccessException {
		return delete(sql , null);
	}
	
	public int delete(final String sql , final Object obj) throws DataAccessException {
		return execute(new SqlMapClientCallback() {

			public Integer doInSqlMapClient(SqlSession sqlSession) {
				// TODO Auto-generated method stub
				return sqlSession.delete(sql , obj);
			}
		});
	}
	
	public void delete(String sql , Object obj , int requiredRowsAffected) throws DataAccessException {
		int i = delete(sql , obj);
		if(i != requiredRowsAffected){
			throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(sql , requiredRowsAffected , i);
		}
	}
	
	/**数据删除 + EhCache*/
	public int deleteEhCache(String sql) throws DataAccessException {
		return deleteEhCache(sql , null);
	}
	
	public int deleteEhCache(final String sql , final Object obj) throws DataAccessException {
		int result = execute(new SqlMapClientCallback() {

			public Integer doInSqlMapClient(SqlSession sqlSession) {
				// TODO Auto-generated method stub
				return sqlSession.delete(sql , obj);
			}
		});
		
		if(result > 0){
			logger.debug("更新记录成功，移除缓存中相应记录。");
			updateEhCacheElement(sql);
		}
		
		return result ;
	}
	
	public void deleteEhCache(String sql , Object obj , int requiredRowsAffected) throws DataAccessException {
		int i = delete(sql , obj);
		if(i != requiredRowsAffected){
			throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(sql , requiredRowsAffected , i);
		}
		else {
			logger.debug("更新记录成功，移除缓存中相应记录。");
			updateEhCacheElement(sql);
		}
	}
	
	
	/**Map*/
	/*public <K , V> Map<K , V> queryForMap(String )*/
	
	
	interface SqlMapClientCallback<T> {
		
		T doInSqlMapClient(SqlSession sqlSession);
		
	}



	/*public SqlMapSession startSession(boolean paramBoolean)
			throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}*/
	

}
